package com.ming_za.odupdaterserver.core.service.res;

import com.ming_za.odupdaterserver.api.ResourcesManager;
import com.ming_za.odupdaterserver.core.config.ConfigManager;
import com.ming_za.odupdaterserver.core.config.type.StringValue;
import com.ming_za.odupdaterserver.core.controller.web.UpdateAPI;
import com.ming_za.odupdaterserver.core.exception.BusinessException;
import com.ming_za.odupdaterserver.core.exception.ROEnum;
import com.ming_za.odupdaterserver.core.service.ReturnBusinessPackage;
import com.ming_za.odupdaterserver.core.service.res.cache.ResCacheOptions;
import com.ming_za.odupdaterserver.core.service.res.cache.ResInternalCache;
import com.ming_za.odupdaterserver.core.sqlite.domain.Res;
import com.ming_za.odupdaterserver.core.utils.BinaryUtils;
import com.ming_za.odupdaterserver.core.utils.FileUtils;
import com.ming_za.odupdaterserver.core.utils.Msg;
import com.ming_za.odupdaterserver.core.utils.SnowflakeIdGenerator;
import com.ming_za.odupdaterserver.core.utils.cache.MapInternalCache;
import com.ming_za.odupdaterserver.core.vo.req.ResAddVo;
import com.ming_za.odupdaterserver.core.vo.req.SetResClientPathVo;
import com.ming_za.odupdaterserver.core.vo.req.SetResLabelVo;
import lombok.Getter;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.*;

/**
 * 资源管理器,接口实现类
 * <p>负责对资源的管理，存储，修改，检索等</p>
 * <p>如需调用更新功能请查看{@link com.ming_za.odupdaterserver.api.Updater}</p>
 * <p>如需调用资源功能请查看{@link ResourcesManager}</p>
 */
@Service
public class ResourcesManagerImpl implements ResourcesManager {

    private final SnowflakeIdGenerator snowflakeIdGenerator;
    private final FileUtils fileUtils;
    private final BinaryUtils binaryUtils;
    private final FileUtils.NormalTransmitter normalTransmitter;

    /**
     * 资源文件夹
     */
    @Getter
    private File resFold;

    /**
     * 文件后缀
     */
    private String fileSuffix;

    /**
     * 资源缓存
     */
    private final ResInternalCache resCache;

    public ResourcesManagerImpl(SnowflakeIdGenerator snowflakeIdGenerator, FileUtils fileUtils, BinaryUtils binaryUtils, FileUtils.NormalTransmitter normalTransmitter, ResCacheOptions resCacheOptions, ConfigManager configManager) {
        this.snowflakeIdGenerator = snowflakeIdGenerator;
        this.fileUtils = fileUtils;
        this.binaryUtils = binaryUtils;
        this.normalTransmitter = normalTransmitter;
        resCache = new ResInternalCache(resCacheOptions);

        StringValue resFoldSV = configManager.getResFold();

        resFold = new File(resFoldSV.getValue());
        tryMkdirRes(resFold);
        resFoldSV.bindListener(value->{
            resFold = new File(configManager.getResFold().toString());
            tryMkdirRes(resFold);
        });

        StringValue fileSuffixSV = configManager.getResFileSuffix();
        fileSuffix = fileSuffixSV.getValue();
        fileSuffixSV.bindListener(value-> fileSuffix = value);

    }

    private void tryMkdirRes(File fold) {
        if (!fold.exists()){
            if (!fold.mkdirs()){
                throw new BusinessException(ROEnum.UNEXPECTED_ERROR,"资源文件夹创建失败！");
            }
        }
    }

    @Override
    public boolean checkLocalRes(long resId) {
        Res res = getRes(resId);
        File file = new File(res.getLocalPath());
        return file.exists();
    }

    /**
     * 获取Res资源对象
     * <pre>
     * 先从缓存中获取数据,如果缓存中不存在，则到数据库中获取
     * 如果数据库中存在,而且缓存开启，则将数据放到缓存中
     * </pre>
     * @param resId 资源id
     * @return {@link Res}资源数据对象
     */
    @Override
    public Res getRes(long resId) {
        return resCache.getValue(resId);
    }

    @Override
    public List<Res> getRes(Long[] ids) {
        if (ids.length != 0){
            return new ArrayList<>(resCache.getValues(ids));
        }else {
            return new ArrayList<>();
        }
    }

    @Override
    public FileInputStream getResInputStream(long resId) throws FileNotFoundException {
        Res res = getRes(resId);
        if (res == null || res.getDelete()){
            return null;
        }
        return new FileInputStream(res.getLocalPath());
    }

    @Override
    public ReturnBusinessPackage<Long> addRes(ResAddVo res, InputStream fileInput) {
        Res resObj = new Res();
        long resId = snowflakeIdGenerator.nextId();

        if (!res.getDelete()){

            if (fileInput == null){
//                throw new BusinessException(ROEnum.UPDATE_NEED_FILE,"非删除功能的更新需要文件！");
                return new ReturnBusinessPackage<>(ROEnum.UPDATE_NEED_FILE.setMsg("非删除功能的更新需要文件！"),null);
            }

            //创建文件并保存
            File resFile = new File(resFold,resId + fileSuffix);
            if (resFile.exists()){
//                throw new BusinessException(ROEnum.UNEXPECTED_ERROR,"意外错误！资源文件已存在！->" + resFile.getName());
                return new ReturnBusinessPackage<>(ROEnum.UNEXPECTED_ERROR.setMsg("意外错误！资源文件已存在！->" + resFile.getName()),null);
            }
            try {
                if (!resFile.createNewFile()){
//                    throw new BusinessException(ROEnum.FILE_UNABLE_TO_CREATE);
                    return new ReturnBusinessPackage<>(ROEnum.FILE_UNABLE_TO_CREATE,null);
                }
                FileOutputStream fileOutputStream = new FileOutputStream(resFile);
                fileUtils.streamTransmit(fileOutputStream,fileInput,normalTransmitter);
                fileOutputStream.flush();
                fileOutputStream.close();

            } catch (IOException e) {
//                throw new BusinessException(e.getMessage(),e,ROEnum.FILE_UPDATE_FAIL);
                return new ReturnBusinessPackage<>(ROEnum.FILE_UPDATE_FAIL,null);
            }
            resObj.setLocalPath(resFile.getPath());
        }

        //将资源信息数据添加到数据库
        resObj.setResid(resId);

        String clientPath = res.getClientPath();

        if (clientPath.contains("\\")){
            clientPath = clientPath.replaceAll("\\\\","/");
        }

        if (clientPath.startsWith("./")){
            resObj.setClientPath(clientPath);
        }else {
            resObj.setClientPath("./" + clientPath);
        }
        resObj.setNecessary(res.getNecessary());
        resObj.setDelete(res.getDelete());
        resObj.setTime(new Date(System.currentTimeMillis()));
        resObj.setLabel(res.getLabel());
        resCache.putValue(resObj.getResid(),resObj);

//        return resId;
        return new ReturnBusinessPackage<>(true,ROEnum.SUCCESS,resId);
    }

    /**
     * 使用标签获取相关资源
     * @param label 标签不能为复合标签数据
     * @return 相关的资源
     */
    public ReturnBusinessPackage<List<Res>> getResIncludeLabel(long label) {

        if (!binaryUtils.onlyOneBit(label)){
//            throw new BusinessException(ROEnum.RES_LABEL_NOT_ALONE);
            return new ReturnBusinessPackage<>(ROEnum.RES_LABEL_NOT_ALONE,null);
        }

//        return resCache.getResRelatedToLabel(label);
        return new ReturnBusinessPackage<>(true,ROEnum.SUCCESS,resCache.getResRelatedToLabel(label));
    }

    @Override
    public List<Res> getDefaultLabelRes() {

        List<Res> result = new ArrayList<>();

        for (Res res:resCache.getCacheData().values()){
            if (res.getLabel() == 0){
                result.add(res);
            }
        }

        return result;
    }

    @Override
    public void setResLabel(SetResLabelVo[] setResLabelVo) {
        if (setResLabelVo.length < 1){
            return;
        }
        Long[] ids = Arrays.stream(setResLabelVo).map(SetResLabelVo::getResId).toList().toArray(new Long[0]);
        Set<Res> values = resCache.getValues(ids);
        for (SetResLabelVo pathVo:setResLabelVo){
            for (Res res:values){
                if (res.getResid().equals(pathVo.getResId())){
                    res.setLabel(pathVo.getLabel());
                }
            }
        }
        resCache.save(new ArrayList<>(values));
    }

    @Override
    public void setClientPath(SetResClientPathVo[] setResClientPathVos) {
        Long[] ids = Arrays.stream(setResClientPathVos).map(SetResClientPathVo::getResId).toList().toArray(new Long[0]);
        Set<Res> values = resCache.getValues(ids);
        for (SetResClientPathVo pathVo:setResClientPathVos){
            for (Res res:values){
                if (res.getResid().equals(pathVo.getResId())){
                    res.setClientPath(pathVo.getPath());
                }
            }
        }
        resCache.save(new ArrayList<>(values));
    }

    @Override
    public void removeRes(Long[] resIds) {

        if (resIds.length == 0){
            return;
        }

        Set<Long> successDelete = new HashSet<>();
        Set<Res> resSet = resCache.getValues(resIds);

        if (resSet.isEmpty()){
            return;
        }

        for (Res res:resSet){
            if (!res.getDelete()){
                File file = new File(res.getLocalPath());
                if (!file.delete()){
                    //删除失败后尝试将传输中的inputStream断开再次删除
                    InputStream transmission = UpdateAPI.getTransmission().get(res.getResid());
                    if (transmission != null){
                        try {
                            transmission.close();
                            if (!file.delete()){
                                Msg.getInstance().warn("[删除资源]本地数据文件删除失败！->" + res.getLocalPath());
                                continue;
                            }
                        } catch (IOException ignored) {
                        }
                    }
                }
            }
            successDelete.add(res.getResid());
        }

        resCache.removeValues(new ArrayList<>(successDelete));
    }

//    /**
//     * 获取资源，如果资源位null则抛出找不到资源异常
//     * @param resId 资源id
//     */
//    private Res getResThrowNullException(long resId) {
//        Res res = getRes(resId);
//        if (res == null){
//            throw new BusinessException(ROEnum.UPDATE_NOTFOUND_RES);
//        }
//        return res;
//    }

}
